#include "_public.h"

#define MAXNUMP_    1000           // 最大进程数量
#define SHMKEYP_    0x5095         // 共享内存的key
#define SEMKEYP_    0x5095

struct  st_pinfo
{
    int pid;            // 进程id
    char pname[51];     // 进程名称
    int timeout;        // 超时时间
    time_t atime;       // 最后一次心跳时间
};


int main(int argc, char *argv[]){
    if(argc < 2){
        cout << "USING: ./book procname" << endl;
        return 0;
    }
    
    // 创建/获取共享内存
    int m_shmid = 0;
    if((m_shmid = shmget(SHMKEYP_, MAXNUMP_ * sizeof(struct st_pinfo), 0640|IPC_CREAT)) == -1){
        cout << "shmget(" << MAXNUMP_ << ") failed " << endl;
        return -1;
    }

    CSEM m_sem;
    if((m_sem.init(SEMKEYP_) == false)){
        cout << "m_sem (" << SEMKEYP_ << ") failed." << endl;
        return -1;
    }

    // 将共享内存连接到当前进程的地址空间
    struct st_pinfo *m_shm;
    m_shm = (struct st_pinfo *)shmat(m_shmid, 0, 0);

    // 创建当前进程的心跳结构体变量，输入该进程的信息
    struct st_pinfo stpinfo;
    memset(&stpinfo, 0, sizeof(struct st_pinfo));
    stpinfo.pid = getpid();
    STRNCPY(stpinfo.pname, sizeof(stpinfo.pname), argv[1], 50);
    stpinfo.timeout = 30;
    stpinfo.atime = time(0);

    int m_pos = -1;
    /*
     * 进程ID是循环使用的，若有一个进程曾经异常退出，没有清理自己的心跳信息
     * 它的进程信息会残留在共享内存中，若当前进程重用了上述进程的ID
     * 就会在共享内存中存在两个进程ID相同的记录，守护进程检测到残留进程心跳时
     * 会向进程ID发出退出信号，误杀当前进程
     */
    
    // 若共享内存中存在当前进程编号，一定是其它进程残留的数据，当前进程重用该位置
    for(int i = 0; i < MAXNUMP_; ++i){
        if(m_shm[i].pid == stpinfo.pid){
            m_pos = i;
            break;
        }
    } 

    m_sem.P();
    // 在共享内存中找一个空位置，把当前进程的心跳信息存入共享内存中
    if(m_pos == -1){
        for(int i = 0; i < MAXNUMP_; ++i){
            if(m_shm[i].pid == 0){
                // 找到一个空位置
                m_pos = i;
                break;
            }
        }
    }
    
    if(m_pos == -1){
        m_sem.V();
        cout << "shared memory has been all used." << endl;
        return -1;
    }
    memcpy(m_shm+m_pos, &stpinfo, sizeof(struct st_pinfo));

    m_sem.V();

    while(true){
        // 更新共享内存中该进程的心跳时间
        m_shm[m_pos].atime = time(0);
        
        sleep(10);
    }

    // 把当前进程从共享内存中移去
    memset(m_shm+m_pos, 0, sizeof(struct st_pinfo));

    // 把共享内存从当前进程中分离
    shmdt(m_shm);

    return 0;
}
